// webpack.config.js文件与src目录同级
// webpack.config.js是配置文件，当运行webpack命令时，会执行配置里的设置
// 构建工具是基于nodejs平台的，和src目录下编写的代码（基于ES6）不同，webpack.config.js中是使用commonjs的

// resolve是nodejs的语法,用于拼接绝对路径的方法
const { resolve } = require('path');
//! 引入下载的html-webpack-plugin插件包,用于打包html文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
// !引用下载的mini-css-extract-plugin插件包,用于将js中的css资源提取出来成一个css文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// !引用下载的optimize-css-asset-ewbpack-plugin插件包,用于将css文件压缩成一行,一旦使用这个，即使通过环境变量设置为开发模式，css仍然会被压缩成一行
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
// !在重新打包前自动删除上一次的打包文件，注意：这个CleanWebpackPlugin要用{}包起来，否则报错
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
//! 通过copy-webpack-plugin这个拷贝插件，将选择的目录下所有文件原封不动的复制到打包后的文件夹中去
const CopyWebpackPlugin = require('copy-webpack-plugin');  //因为高版本有冲突，所以要安装npm i copy-webpack-plugin@5.0.0 -D 才不会报错

// npm i uglifyjs-webpack-plugin@1.1.1 -D  只有这个版本能运行清空js中的console.log()语句了
// uglifyjs-webpack-plugin 用于打包后，清空 js文件 中的console.log()语句  无法清除html中js的console.log()
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

// nodejs中的环境变量,表示当前导报按照开发模式打包(兼容css中的loader需要这个来声明以开发模式打包,否则默认为生产模式打包)
// 但是只要使用optimize-css-assets-webpack-plugin（用于将css文件压缩成一行），即使通过环境变量设置为开发模式，css仍然会被压缩成一行
// process.env.NODE_ENV = 'development';

// commonjs暴露对象，再其内部配置webpack
module.exports = {
  // 入口设置，只是webpack从哪里开始打包
  /**
   * entry的写法：
   * entry:'./src/index.js'  这种直接string字符串的方式，表示单入口，就是只有一个js入口，输出一个chunk(打包为一个js文件)，名字默认是main.js
   * entry:['./src/index.js','./src/index2.js']  这种直接数组的方式，表示有多个个js入口，但是也输出一个chunk(打包为一个js文件)，名字默认是main.js
   * entry: { }  这种对象形式，是多文件入口，每个key（就是名字） 对应一个入口js文件，输出多个chunk，且chunk名称为设置的key
   */
  entry: {
    indexJs: './src/index.js',// 页面1
    // aboutJs: './src/index.js', // 页面2
  },
  output: {
    // 打包后输出的文件名
    // ! [name]====>是根据entry中设置的indexJs来命名的，这里的[name]就是indexJs,这样可以让多页面打包时，各个页面不混乱
    filename: 'js/[name]/[name]-built.js',
    // 输出文件的路径
    // __dirname是nodejs的变量,表示当前文件(webpack.config.js)目录的绝对路径
    // resolve(__dirname,'build')表示当前路径下的build文件夹
    path: resolve(__dirname, 'build'),

    //!publicPath表示在打包后，页面中引入资源时的src处的公共前缀 
    //!【可能会造成找到文件:因为不设置公共路径的时候，会根据生成目录自动../../找到所需资源文件，但是加了公共路径，就不会自动添加../这些了】
    /**
     * <link rel="stylesheet" href="css/tes.css">   没有设置公共路径'/'时，打包后引入资源时的路径
       <link rel="stylesheet" href="/css/tes.css">  有设置公共路径'/'时，打包后引入资源时的路径会有这个公共路径前缀   /表示当前项目的根路径
     */
    // publicPath: '/'  

  },
  // loader的配置写在module中,用于打包css\img等
  module: {
    // 详细的loader配置
    rules: [
      // 打包css和less资源
      {
        // test表示通过正则表达式进行入口文件中引入的文件名尾缀匹配
        test: /\.(css|less)$/, //表示只针对入口文件中引入的css、less文件
        // 通过use设置要进行哪些loader处理
        //! use中的设置,执行顺序是从下到上,从右到左,所以,是先执行css-loader,再执行style-loader
        // !!! 想要使用这些loader或者plugins插件,要下载包:  npm i css-loader style-loader -D (注意-D,要安装到开发依赖中去)
        use: [
          //style-loader 会将js中的样式资源插入到页面的<header>标签中(自动创建一个<style>标签插入样式),这样打包的css是合并在js文件中的,js体积大,加载慢,有闪屏
          // 'style-loader',
          // 提取js中的css资源,单独成一个css文件,解决js过大,加载闪屏的问题
          MiniCssExtractPlugin.loader,
          // css-loader 将css文件变成commonjs模块,加载到js文件中,里面的内容是字符串
          'css-loader',
          /***
           * 直接写 'css-loader'  <====> {loader: 'css-loader'}
           * 只不过,想要能设置options{},就必须loader: 'css-loader',这样写才行
           */
          // !设置css打包时的兼容性
          /***
           * !使用方式为：在webpack.config.js同级下新建postcss.config.js文件，里面写上调用语句postcss-preset-env
           * !需要使用到 postcss-loader 和 postcss-preset-env 这两个包,得npm安装
           * !同时要在package.json文件中配置 browserslist属性,这个属性里配置了指定的css兼容样式
           */
          'postcss-loader',
          // 首先通过less-loader将less文件解析成css格式,然后跟处理css一样
          'less-loader'
        ]
      },
      // 打包less资源也可以单独写出来
      // {
      //   test: /\.less$/,
      //   use: [
      //     MiniCssExtractPlugin.loader,
      //     'css-loader',
      //     // 首先通过less-loader将less文件解析成css格式,然后跟处理css一样
      //     'less-loader'
      //   ]
      // },

      // 打包css中使用url引入的图片资源
      {
        // 针对文件尾缀为jpg\png\gif格式的文件
        test: /\.(jpg|png|gif)$/,
        // 因为只使用一个loader,所以可以不用use[]来写
        //! 需要下载两个loader:url-loader和file-loader,因为两者有从属关系
        loader: 'url-loader',
        // 打包后输出的参数设置
        options: {
          // 针对图片大小小于8kb的,直接不打包,而是转换成base64格式,减少请求
          limit: 8 * 1024,
          // 因为打包后的文件根据hash重命名,很长,通过[hash:10]这个设置只保留10位,防止名字过长
          // [ext] 表示根据原文件的尾缀重命名打包后的文件
          name: '[hash:10].[ext]',
          // 因为要打包html中的图片,而html-loader无法理解ES6,所以要关闭url-loader中的ES6格式
          esModule: false,
          //? 图片资源打包后输出时存放到一个叫做imgs的文件夹下
          outputPath: 'imgs'
        }
      },
      // 打包html中的img资源  还有对html文件的其他功能
      /**
       * !只要有使用html-loader这个插件，那么在使用HtmlWebpackPlugin获取html文件处理时，源文件就不能带有link/script这些标签，
       * 否则会报错 Callback was already called   回调已被调用  注释掉源文件中的资源引用就不会报错了
       * !针对多文件，想要保留源文件中的link/script引入，就不要使用html-loader这个插件，那么使用HtmlWebpackPlugin时也不会清除源文件的引入了
       */
      {
        test: /\.html$/,
        // 要和url-loader中的esModule: false搭配使用,否则报错
        // 因为html-loader中引入图片是commonjs,无法用es6解析
        loader: 'html-loader'
      },
      // 打包其他资源(如:字体\图标)
      {
        // exclude除去html|js|css|less|jpg|png|gif这些格式,打包其他的
        exclude: /\.(html|js|css|less|jpg|png|gif)/,
        loader: 'file-loader',
        options: {
          name: '[hash:10].[ext]',
          //? 其他资源打包后输出时存放到一个叫做media的文件夹下
          outputPath: 'media'
        }
      }

    ]
  },
  // 插件配置,plugins,插件功能更加强大
  // !plugins跟loader相似,也是要通过npm下载,但是要引入才能使用
  plugins: [
    //使用插件 html-webpack-plugin这个包用来打包html 文件
    //! 插件下载和loader下载一样,npm i 插件名 -D
    //使用引入的这个插件,会创建一个空的HTML,并且自动引入打包的资源(如:js/css等)
    // 但是一般我们主页都是写好的,所以通过参数,让它用我们自己的主页,自动引入打包后的资源
    // new HtmlWebpackPlugin()
    new HtmlWebpackPlugin({
      //HtmlWebpackPlugin中的[name]表示的不是entry中的chunks的值，而是HtmlWebpackPlugin_1这样的
      // ! 不设置filename（输出文件名和位置） 那么默认是index.html
      // 复制这个HTML文件,并自动将打包后的资源引入，（注意：原页面中不能再引入js/css这些了，否则会引入两次）
      template: './src/html/index.html',
      // chunks是根据入口js文件哪里设置名称决定的，用于区分多页面的
      // 如果不指定chunks的话会把所有的js都引入，根据指定的chunks，打包后的页面中就只会引入入口js文件中的js文件
      chunks: ['indexJs'],
      hash: true, //为了更好的 cache，可以在生成的 html 文件中引入的资源文件的src后拼接 ?hash值
      minify: {
        collapseWhitespace: true,// 清除HTML中所有空格
        removeComments: true,// 清除所有注释
        //!是否压缩html内联css（使用clean-css进行的压缩） 默认值false
        minifyCSS: true,
        //!是否压缩html里的js（使用uglify-js进行的压缩） 默认值false
        minifyJS: true,
      }
    }),
    new HtmlWebpackPlugin({
      //HtmlWebpackPlugin中的[name]表示的不是entry中的chunks的值，而是HtmlWebpackPlugin_1这样的
      // !设置页面打包后的输出位置和名称
      filename: 'html/about/about.html',
      // ! 注意：inject: false 会导致打包后页面中自带的link、script的src地址变成[object Object]
      // inject: false,//默认为true，false表示不用自动引入打包后的js/css等资源，防止和原页面中引入的资源重复而报错
      // 复制这个HTML文件,并自动将打包后的资源引入，（注意：原页面中不能再引入js/css这些了，否则会引入两次）
      template: './src/html/about.html',
      // chunks是根据入口js文件哪里设置名称决定的，用于区分多页面的
      // 如果不指定chunks的话会把所有的js都引入，根据指定的chunks，打包后的页面中就只会引入入口js文件中的js文件
      chunks: ['aboutJs'],
      hash: true, //为了更好的 cache，可以在生成的 html 文件中引入的资源文件的src后拼接 ?hash值
    }),

    //! 通过copy-webpack-plugin这个拷贝插件，将选择的目录下所有文件原封不动的复制到打包后的文件夹中去
    /**
     * 通过这样复制过去的，原封不动，所以在打包后的html中加上link/script原来的路径，就能引用到文件
     */
    new CopyWebpackPlugin(
      [{ from: "./src/res", to: "public" }]  // 需要拷贝的目录  打包后的存放目录 
    ),

    // 使用插件mini-css-extract-plugin,将js文件中的css资源提取出来
    // 用这个插件提取js中的css,那么在loader中处理css的时候,'style-loader'就不能要了,因为不再是通过<style>标签插入css了
    // !所以要替换成MiniCssExtractPlugin.loader,进行css提取
    new MiniCssExtractPlugin({
      // 对输出的打包后的css文件命名并指定存放的位置(指在build文件夹下的位置)
      filename: 'css/[name]-[hash:5].css'
    }),
    // 将js文件中的console.log语句删除，html中的js的console.log语句无法通过这个方法删除
    // ~ 手动通过全局搜索 使用 console\.log\(.*?\);  和  console\.log\(.*?\)  利用正则表达式，进行查找到所有console.log,全局替换掉再打包
    new UglifyJsPlugin({
      uglifyOptions: {
        compress: {
          warnings: false,
          drop_console: true,//console
          pure_funcs: ['console.log']//移除console
        }
      }
    }),
    // 只要引用这个压缩css的插件,就能压缩css文件
    new OptimizeCssAssetsWebpackPlugin(),
    new CleanWebpackPlugin()//实例化，参数为目录
  ],
  // 设置打包的模式
  // mode: 'development', //开发模式打包
  mode: 'production', //生产模式打包,自动将js、html压缩成一行

  /**
   * 会自动将node_modules中的代码单独打包出来  （单页面（单入口）打包时自动使用）
   * !会自动将 多入口打包时的各个 chunk中的公共文件（比如多个入口js中都用到了jq） 单独打包成一个chunk，共用它，防止重复打包 (但是这个要单独打包的不能太小，否则没有单独打包的必要)
   */
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },

  // ! 拒绝/忽略打包的库  这里设置了jq，也就说即使入口js文件中引入了jq，也不会打包它，但是：会导致打包后的html中没有引入拒绝打包的js文件，要手动script标签引入
  // externals: {
  //   jquery: 'jQuery'
  // },

  // 解析模块的规则 配置路径的别名
  resolve: {
    // 配置解析模块路径的别名--》resolve(__dirname, './src/css')
    // !优点：简写路径，多级嵌套目录中能直接锁定目标目录路径
    // !缺点：使用简写，无法弹出路径提示，容易出错
    /***
     * !resolve(__dirname, './src/css')表示：通过 './src/css'找到这个目录位置
     * 然后，通过__dirname 在任何子目录下，都能找到到这个指定目录位置，就不用自己在通过../../这样找到了
     * !指定为这个$css别名，只要写$css在任何子目录下都能直接表示'src/css'这个位置，相当于省去了../这些操作
     */
    alias: {
      // $css就是自己设置的src目录css目录的绝对路径的别名，其他无论多深的目录嵌套，都能用这个$css直接找到
      $css: resolve(__dirname, './src/css')
    }
  },
  // !打包代码前后的映射，用于查找错误
  // 用于在开发阶段，代码出错，根据打包前后代码映射关系，找到打包前源码中的错误位置
  // !会在打包后的 每个js文件一起 生成.map文件 ，里面是这个js文件的打包前后的代码映射关系
  devtool: 'source-map'

  // !热部署,修改后不用重新在控制台输入webpack重新打包,自动打开游览器,监视文件的改变,自动更新打包
  // 特点:不会生成打包文件(不会生成build文件夹),只是在内存中打包然后运行,关闭后自动清除内存
  // !要安装webpack-dev-server这个包 npm i webpack-dev-server -D
  // !启动webpack热部署的命令行:npx webpack-dev-server  (因为是本地的包,所以要启动不能用npm,而是要用npx来启动)
  // 不用npx这个命令启动,还是用webpack命令的话,这几行代码没有影响的
  // devServer: {
  //   // 要热运行的目录,也就是打包一次后的生成的目录
  //   contentBase: resolve(__dirname, 'build'),
  //   // 启动gzip压缩,让热部署代码体积更小,运行更快
  //   compress: true,
  //   // 监视contentBase目录下所有文件的改变，只要发生变化，直接重新打包，并且刷新游览器
  //   // watchContentBase: true,  //如果写成对象模式，默认就为true开启了
  //   watchContentBase: {
  //     // 忽略监视的目录  （有些文件不需要监视，比如node_modules）
  //     ignored: /node_modules/
  //   },
  //   // 设置打开域名
  //   host: 'localhost',
  //   // 设置localhost:3000为打开的端口号(自定义端口号)
  //   port: 3000,
  //   // 自动根据设置的端口号打开游览器
  //   open: true,
  //   // 不打印显示启动服务器的日志，不然太多了，而且没啥用
  //   clientLogLevel: 'none',
  //   // 直显示一些服务其启动的基本信息，其他的信息不打印显示,其他没必要看
  //   quiet: true,
  //   // 设置服务器代理（用于前后端分离，又使用内网穿透来获取后端数据时使用）
  //   proxy: {
  //     // 当我们自己启动的这个devServer（端口号为3000）服务器收到/api/xx的请求时，就会把请求发送到target设置的这个网址去（后端的地址）
  //     // 这里的样例表示会被转发成http://localhost:5000/api/xx  这样就能找到后端的数据
  //     'api': {
  //       target: 'http://localhost:5000',
  //       // pathRewrite--->是在发送请求时，重写请求路径的，这里样例的意思是：
  //       // '^/api': ''===》将/api转写成'' （就是删除 /api）  也就是转发成http://localhost:5000/xx  只留下了xx，删除了/api
  //       pathRewrite: {
  //         '^/api': ''
  //       }
  //     }
  //   }
  // }

}